import { ETTask } from "../../Support/Share/Lib/Async/ETTask";
import { ISingleton, Singleton } from "./Singleton";
import { Dictionary } from "../../Support/Share/Lib/Dictionary";
import { Log } from "../Module/Log/Log";
import { IsInstanceOf, IsNotInstanceOf } from "../../Decorator/Decorator";
import { Queue } from "../../Support/Share/Lib/Collections/Queue";
import { Stack } from "../../Support/Share/Lib/Collections/Stack";
import { ISingletonAwake } from "./ISingletonAwake";
import { ISingletonLateUpdate } from "./ISingletonLateUpdate";
import { ISingletonUpdate } from "./ISingletonUpdate";

export class Game {

    private static readonly singletonTypes = new Dictionary<string, ISingleton>();
    private static readonly singletons: Stack<ISingleton> = new Stack<ISingleton>();
    private static readonly updates: Queue<ISingleton> = new Queue<ISingleton>();
    private static readonly lateUpdates: Queue<ISingleton> = new Queue<ISingleton>();
    private static readonly frameFinishTask: Queue<ETTask<any>> = new Queue<ETTask<any>>();

    public static AddSingleton<T extends Singleton<T>>(clazz: { new(): Singleton<T> }) {
        let singleton = new clazz();
        this.AddSingletonWith(singleton);
        return singleton as T;
    }

    public static AddSingletonWith(singleton: ISingleton) {
        let singletonType = singleton.constructor.name;
        if (this.singletonTypes.ContainsKey(singletonType)) {
            throw new Error(`already exist singleton: ${singletonType}`);
        }
        this.singletonTypes.Add(singletonType, singleton);
        this.singletons.Push(singleton);

        singleton.Register();

        if (IsInstanceOf(singleton, ISingletonAwake)) {
            (<ISingletonAwake><unknown>singleton).Awake();
        }
        if (IsInstanceOf(singleton, ISingletonUpdate)) {
            this.updates.Enqueue(singleton);
        }
        if (IsInstanceOf(singleton, ISingletonLateUpdate)) {
            this.lateUpdates.Enqueue(singleton);
        }
    }

    static Update() {
        let count = this.updates.Count;
        while (count-- > 0) {
            let singleton = this.updates.Dequeue();
            if (singleton.IsDisposed()) {
                continue;
            }
            if (IsNotInstanceOf(singleton, ISingletonUpdate)) {
                continue;
            }
            this.updates.Enqueue(singleton);
            try {
                (<ISingletonUpdate><unknown>singleton).Update();
            } catch (e) {
                Log.Error(e);
            }
        }
    }

    static LateUpdate() {
        let count = this.lateUpdates.Count;
        while (count-- > 0) {
            let singleton = this.lateUpdates.Dequeue();
            if (singleton.IsDisposed()) {
                continue;
            }
            if (IsNotInstanceOf(singleton, ISingletonLateUpdate)) {
                continue;
            }
            this.lateUpdates.Enqueue(singleton);
            try {
                (<ISingletonLateUpdate><unknown>singleton).LateUpdate();
            } catch (e) {
                Log.Error(e);
            }
        }
    }

    static FrameFinishUpdate() {
        while (this.frameFinishTask.Count > 0) {
            let task = this.frameFinishTask.Dequeue();
            task.SetResult();
        }
    }

    static Close() {
        // 顺序反过来清理
        while (this.singletons.Count > 0) {
            let iSingleton = this.singletons.Pop();
            iSingleton.Destroy();
        }
        this.singletonTypes.Clear();
    }

}